
#include <config.h>
#include <video.h>
#include <klib.h>
#include <gdt.h>

GDT       __GDT_reg;
GDT_entry gdt[3];

void _print_gdt_entry(GDT_entry *);

/**
 * Inits the global desciptor table with a flat, 32bit address space.
 * The actual table will have 
 */
void gdt_init(){

#  ifdef CONF_SYS_AMD64

#  else
  __GDT_reg.limit = 23; // 3 * 8 - 1
  __GDT_reg.base = (uint32) &gdt;
  _build_gdt();
  /* Test        */
  GDT_entry dupe;
  dupe.base_low = 0xffff;
  dupe.base_mid = 0;
  dupe.base_high = 0;
  dupe.limit_low = 0xffff;
  dupe.limit_high = 0xf;
  dupe.flags_low = 0xf;
  dupe.flags_high = 0xf;
  CODE_SEG((&dupe));
  _print_gdt_entry(&dupe);
  /* End of test */
  _print_gdt_entry(&(gdt[1]));
  _print_gdt_entry(&(gdt[2]));
  for (;;);
  _flush_gdt();
#  endif

}

void _build_gdt(){

#  ifdef CONF_SYS_I386
  kzero((char *)&(gdt[0]), sizeof(GDT_entry));
  _gdt_make_entry(&(gdt[1]), 0, 0xfffff, 
		  SEG_PRESENT|SEG_GRAN|SEG_DPL_SYS|SEG_OP_SIZE);
  _gdt_make_entry(&(gdt[2]), 0, 0xfffff, 
		  SEG_PRESENT|SEG_GRAN|SEG_DPL_SYS|SEG_OP_SIZE);

  CODE_SEG((&(gdt[1])));
  DATA_SEG((&(gdt[2])));
#  endif

}

#  ifdef CONF_SYS_I386
void _gdt_make_entry(GDT_entry *desc, uint32 base, uint32 limit, uint8 flags){

  desc->flags_high = flags >> 4;
  desc->flags_low  = flags & 0xf0;
  desc->limit_high = (limit >> 16) & 0xfff0;
  desc->limit_low  = limit & 0xffff0000;
  desc->base_high  = base >> 24;
  desc->base_mid   = (base >> 16) & 0xff00;
  desc->base_low   = base & 0xffff0000;

}
#  endif

void _print_gdt_entry(GDT_entry *entry){

  char pow2[] = {128, 64, 32, 16, 8, 4, 2, 1};

  char *gdt_entry = (char *) entry;
  int i = 0;
  for ( ; i < 8; i++){
    int j = 0;
    for ( ; j < 8; j++){
      if (gdt_entry[i] & pow2[j]) vid_printc('1');
      else vid_printc('0');
    }
    if (i == 3 || i == 7) vid_println("");
  }

}
